// author fucaihe@pryun.com.cn
// create time 2018-01-13 20:08:14
// 日期时间工具

package kit

import (
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	yearAgo      = "年前"
	monthAgo     = "月前"
	dayAgo       = "天前"
	yesterDayAgo = "昨天"
	hourAgo      = "小时前"
	minuteAgo    = "分钟前"
	secondAgo    = "秒前"
	justAgo      = "刚刚"
	locationTime = "Local"
	// 正则表达式日期匹配 YYYY-MM-dd | YYYY/MM/dd | YYYY_MM_dd
	regexDate = `((((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9]))|(((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9]))|(((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9]))|(([2468][048]00)([-\/\._])(0?2)([-\/\._])(29))|(([3579][26]00)([-\/\._])(0?2)([-\/\._])(29))|(([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29))|(([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29))|(([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29))|(([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29))|(([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29))|(([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)))`
	// 正则表达式日期匹配(完全)
	regexDatePerfect = `((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))`
	// 正则表达式时间匹配 HH:mm:ss | HH:mm | H:m:s | H:m
	regexTime = `(((20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d)|((20|21|22|23|[0-1]?\d):[0-5]?\d))`
)

var (
	// 日期sep
	dateSepArr = []string{"年", "月", "日", "号"}
	timeSepArr = []string{"时", "分", "秒", "点"}
)

// StrToTimestamp 字符串转时间戳,13位
func StrToTimestamp(layout, value string) (int64, error) {
	loc, err := time.LoadLocation(locationTime) //重要：获取时区,time.Now()获取的也是这个时区,当地时区
	if err != nil {
		return 0, err
	}
	tm, err := time.ParseInLocation(layout, value, loc)
	if err != nil {
		return 0, err
	}
	return tm.UnixNano() / 1000000, nil
}

// TimestampByDuration 获取t时间之后或之前几个小时的时间戳,s为空,则表示当前时间戳
func TimestampByDuration(t time.Time, s string) (int64, error) {
	if IsBlank(s) {
		return t.UnixNano() / 1000000, nil
	}
	duration, err := time.ParseDuration(s)
	if err != nil {
		return 0, err
	}
	return t.Add(duration).UnixNano() / 1000000, nil
}

// DateFormatter 时间格式化
// 刚刚,2秒前,2分钟前,2小时前
// 发布于 2017-11-30
// 编辑于 08:21
// 发布于 19:42
func DateFormatter(s string) (int64, error) {
	now := time.Now()

	if strings.Contains(s, justAgo) {
		return TimestampByDuration(now, "")
	} else if strings.Contains(s, secondAgo) {
		duration := "-" + strings.Replace(s, secondAgo, "s", -1)
		println("wori")
		return TimestampByDuration(now, duration)
	} else if strings.Contains(s, minuteAgo) {
		duration := "-" + strings.Replace(s, minuteAgo, "m", -1)
		return TimestampByDuration(now, duration)
	} else if strings.Contains(s, hourAgo) {
		duration := "-" + strings.Replace(s, hourAgo, "h", -1)
		return TimestampByDuration(now, duration)
	} else if strings.Contains(s, dayAgo) {
		duration := strings.Replace(s, dayAgo, "", -1)
		num, _ := strconv.Atoi(duration)
		num = num * 24
		duration = "-" + strconv.Itoa(num) + "h"
		return TimestampByDuration(now, duration)
	} else if strings.Contains(s, monthAgo) {
		monthStr := strings.Replace(s, monthAgo, "", -1)
		num, _ := strconv.Atoi(monthStr)
		mT := now.AddDate(0, -num, 0)
		return TimestampByDuration(mT, "")
	} else if strings.Contains(s, yearAgo) {
		yearStr := strings.Replace(s, yearAgo, "", -1)
		num, _ := strconv.Atoi(yearStr)
		mT := now.AddDate(-num, 0, 0)
		return TimestampByDuration(mT, "")
	} else if strings.Contains(s, yesterDayAgo) {
		// 昨天 18:11
		mt := now.AddDate(0, 0, -1)
		dateTxtArr := strings.Split(s, yesterDayAgo)
		dateTxt := dateTxtArr[1]
		timeStr := strings.Split(strings.TrimSpace(dateTxt), ":")
		if len(timeStr) == 1 {
			hour := int(GetNumber(timeStr[0])) // 时
			mt = time.Date(mt.Year(), mt.Month(), mt.Day(), hour, 0, 0, 0, mt.Location())
		} else if len(timeStr) == 2 {
			hour := int(GetNumber(timeStr[0])) // 时
			min := int(GetNumber(timeStr[1]))  // 分
			mt = time.Date(mt.Year(), mt.Month(), mt.Day(), hour, min, 0, 0, mt.Location())
		} else if len(timeStr) == 3 {
			hour := int(GetNumber(timeStr[0])) // 时
			min := int(GetNumber(timeStr[1]))  // 分
			sec := int(GetNumber(timeStr[2]))  // 秒
			mt = time.Date(mt.Year(), mt.Month(), mt.Day(), hour, min, sec, 0, mt.Location())
		}
		return TimestampByDuration(mt, "")
	} else {
		// 日期格式转换,年月日='-'
		for _, sep := range dateSepArr {
			s = strings.Replace(s, sep, "-", -1)
		}
		// 时间格式转换,时分秒=':'
		for _, sep := range timeSepArr {
			s = strings.Replace(s, sep, ":", -1)
		}

		// 终极方法,用正则提取日期时间
		var year, day, hour, min, sec, nsec int
		var month time.Month

		// 第一步: 提取日期
		valid := regexp.MustCompile(regexDate)
		dateTxt := ""
		for _, reg := range valid.FindAllString(s, -1) {
			dateTxt = reg
			// 只获取第一个
			break
		}
		// 第二步: 提取时间
		valid = regexp.MustCompile(regexTime)
		timeTxt := ""
		for _, reg := range valid.FindAllString(s, -1) {
			timeTxt = reg
			// 只获取第一个
			break
		}

		// 日期和时间都为空,返回0
		if IsBlank(dateTxt) && IsBlank(timeTxt) {
			return 0, nil
		}

		// 获取日期
		if !IsBlank(dateTxt) {
			dateArr := strings.Split(dateTxt, "-")
			if len(dateArr) <= 1 {
				dateArr = strings.Split(dateTxt, "/")
			}
			if len(dateArr) <= 1 {
				dateArr = strings.Split(dateTxt, "_")
			}
			year = int(GetNumber(dateArr[0]))         // 年
			month = time.Month(GetNumber(dateArr[1])) // 月
			day = int(GetNumber(dateArr[2]))          // 日
		} else {
			// 日期为空,默认当前日期
			year = now.Year()   // 年
			month = now.Month() // 月
			day = now.Day()     // 日
		}

		// 获取时间
		if !IsBlank(timeTxt) {
			timeArr := strings.Split(timeTxt, ":")
			if len(timeArr) == 2 {
				hour = int(GetNumber(timeArr[0])) // 时
				min = int(GetNumber(timeArr[1]))  // 分
			} else if len(timeArr) == 3 {
				hour = int(GetNumber(timeArr[0])) // 时
				min = int(GetNumber(timeArr[1]))  // 分
				sec = int(GetNumber(timeArr[2]))  // 秒
			}
		} else {
			// 时间为空,默认当前时间
			hour = now.Hour()  // 时
			min = now.Minute() // 分
			sec = now.Second() // 秒
		}

		return TimestampByDuration(time.Date(year, month, day, hour, min, sec, nsec, now.Location()), "")
	}
}

// GetCurrentDateStr 获取当前时间的格式化时间字符串
func GetCurrentDateStr(format string) string {
	return time.Now().Format(format)
}

// GetTimestampNow 获取当前时间戳,ms
func GetTimestampNow() int64 {
	return time.Now().UnixNano() / 1000000
}

// GetTimestampSNow 获取当前时间戳,s
func GetTimestampSNow() int64 {
	return time.Now().UnixNano() / 1000000000
}

// GetDateNow 获取当前时间的格式化时间字符串
func GetDateNow(format string) string {
	return time.Now().Format(format)
}

// GetDate 获取时间并格式化字符串
func GetDate(format, duration string) string {
	if d, err := time.ParseDuration(duration); err == nil {
		nextTime := time.Now().Add(d)
		return nextTime.Format(format)
	}
	return ""
}

// ParseStrTimestamp 转换字符串为时间戳
func ParseStrTimestamp(layout, s string) int64 {
	loc, _ := time.LoadLocation("Local") //重要：获取时区
	t, _ := time.ParseInLocation(layout, s, loc)
	return t.UnixNano() / 1000000
}

// GetDateAway 获取距离当前day天的日期,20060102
func GetDateAway(day int, format string) int {
	now := time.Now().AddDate(0, 0, day)
	ymd, _ := strconv.Atoi(now.Format(format))
	return ymd
}

// ParseTimestampStr 时间戳格式化,精确到秒
func ParseTimestampStr(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

// ParseTimestampTime 时间戳转time,精确到秒
func ParseTimestampTime(timestamp int64) time.Time {
	return time.Unix(timestamp, 0)
}
